package offlineFourSquare;

import java.io.Serializable;

import lib.ArgumentValidator;
import lib.logging.ILogger;

public class OfflineFourSquareClientApp 
{
    private static final String checkinCommand = "checkin";
    private static final String exitCommand = "q";
    private static final String helpCommand = "help";
    
    private ILogger logger;
    private IUserInputReader userInputReader;
    private ICommandProcessor commandProcessor;
    
    public OfflineFourSquareClientApp(
            ILogger logger,
            IUserInputReader userInputReader,
            ICommandProcessor commandProcessor)
    {
        ArgumentValidator.IsNotNull("logger", logger);
        ArgumentValidator.IsNotNull("userInputReader", userInputReader);
        ArgumentValidator.IsNotNull("commandProcessor", commandProcessor);
        
        this.logger = logger;
        this.userInputReader = userInputReader;
        this.commandProcessor = commandProcessor;
    }
    
    public void Run()
    {
        logger.LogInfo("Welcome to OfflineFourSquare app!");
        PrintHelp();
        
        while (true)
        {
            try
            {
                logger.LogInfo("Please enter a command (type 'help' to see help menu):");
                
                String input = userInputReader.GetInput();
                
                if (input.trim().equalsIgnoreCase(exitCommand))
                {
                    System.exit(0);
                }
                
                ProcessInput(input);
            }
            catch(IllegalArgumentException ex)
            {
                logger.LogException(ex);
                PrintHelp();
            }
            
            
        }
        
    }

    private void PrintHelp() 
    {
        logger.LogInfo("****** Help Menu **********");
        logger.LogInfo(
                "command: " + checkinCommand +
                "<userID: int> <latitude: double> <longitude: double> <message: optional string>" +
                ": will do a checkin"
                );
        logger.LogInfo("example: " + checkinCommand + " 100 39.5 40.2 This places is awesome!");
        logger.LogInfo("command: " + exitCommand + ": will exit the app.");

    }

    private void ProcessInput(String input) 
    {
        String delimiterIgnoringMultipleSpaces = "[ ]+";
        String tokens[] = input.split(delimiterIgnoringMultipleSpaces);
        
        String command = tokens[0];
        
        if (command.equalsIgnoreCase(checkinCommand))
        {
            int userID;
            double latitude;
            double longitude;
            String message = "";
            
            if (tokens.length >= 4)
            {
                userID = Integer.parseInt(tokens[1]);
                latitude = Double.parseDouble(tokens[2]);
                longitude = Double.parseDouble(tokens[3]);
                
                if (tokens.length > 4)
                {
                    message = ParseMessage(tokens, 4);
                }
                
                commandProcessor.ProcessCheckinCommand(userID, latitude, longitude, message);
            }
            else
            {
                throw new IllegalArgumentException(String.format(
                        "incorrect number or arguments specified. command: '%s', number of arguments: %d.",
                        checkinCommand,
                        tokens.length - 1));
            }
        }
        else if (command.equalsIgnoreCase(helpCommand))
        {
            PrintHelp();
        }
        else
        {
            throw new IllegalArgumentException("unsupported command: " + command);
        }
    }

    private String ParseMessage(String[] tokens, int startIndex) 
    {
        StringBuilder sb = new StringBuilder();
        
        for (int i = startIndex; i < tokens.length; ++i)
        {
            sb.append(tokens[i]);
            sb.append(' ');
        }

        return sb.toString();
    }
}
